// <com.woyouqiu.Copyright>
// --------------------------------------------------------------
// <copyright>上海有求网络科技有限公司 2015</copyright>
// <Solution>MyCompany.MyProject</Solution>
// <Project>MyCompany.MyProject.Application</Project>
// <FileName>OrderAppService.cs</FileName>
// <CreateTime>2017-04-24 0:01</CreateTime>
// <Author>何苗</Author>
// <Email>hemiao@woyouqiu.com</Email>
// <log date="2017-04-24 0:01" version="00001">创建</log>
// --------------------------------------------------------------
// </com.woyouqiu.Copyright>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Linq.Extensions;
using Abp.Runtime.Session;
using Abp.UI;
using Pingpp.Models;
using Vecen.BangChuang.BangChuang.Agents;
using Vecen.BangChuang.BangChuang.Areas;
using Vecen.BangChuang.BangChuang.Areas.DomainDtos;
using Vecen.BangChuang.BangChuang.DomainServices.Agents;
using Vecen.BangChuang.BangChuang.DomainServices.EventHandlers.EventDatas;
using Vecen.BangChuang.BangChuang.DomainServices.Wallets;
using Vecen.BangChuang.BangChuang.Mall.Dtos;
using Vecen.BangChuang.BangChuang.Products;

namespace Vecen.BangChuang.BangChuang.Mall {

    /// <summary>
    /// 订单操作
    /// </summary>
    [AbpAuthorize()]
    public class OrderAppService: BangChuangAppServiceBase, IOrderAppService {
        private readonly IRepository<Order> _orderRepository;
        private readonly IRepository<Product> _productRepository;
        private readonly AgentDomainService _agentDomainService;
        private readonly CityDomainService _cityDomainService;
        private readonly IRepository<UserAddress> _userAddressRepository;
        private readonly PingPlusPlusDomainService _pingPlusPlusDomainService;
        private readonly IRepository<OrderPriceChange> _priceChangeRepository;
        private readonly IRepository<OrderDeliveryInfo> _orderDeliveryInfoRepository;
        private readonly IRepository<Agent> _agentRepository;
        private readonly IRepository<OrderReturn> _orderReturnRepository;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="orderRepository"></param>
        /// <param name="productRepository"></param>
        /// <param name="agentDomainService"></param>
        /// <param name="cityDomainService"></param>
        /// <param name="userAddressRepository"></param>
        /// <param name="pingPlusPlusDomainService"></param>
        /// <param name="priceChangeRepository"></param>
        /// <param name="orderDeliveryInfoRepository"></param>
        /// <param name="agentRepository"></param>
        /// <param name="orderReturnRepository"></param>
        public OrderAppService(IRepository<Order> orderRepository,
            IRepository<Product> productRepository,
            AgentDomainService agentDomainService,
            CityDomainService cityDomainService,
            IRepository<UserAddress> userAddressRepository,
            PingPlusPlusDomainService pingPlusPlusDomainService,
            IRepository<OrderPriceChange> priceChangeRepository,
            IRepository<OrderDeliveryInfo> orderDeliveryInfoRepository,
            IRepository<Agent> agentRepository,
            IRepository<OrderReturn> orderReturnRepository) {
            _orderRepository = orderRepository;
            _productRepository = productRepository;
            _agentDomainService = agentDomainService;
            _cityDomainService = cityDomainService;
            _userAddressRepository = userAddressRepository;
            _pingPlusPlusDomainService = pingPlusPlusDomainService;
            _priceChangeRepository = priceChangeRepository;
            _orderDeliveryInfoRepository = orderDeliveryInfoRepository;
            _agentRepository = agentRepository;
            _orderReturnRepository = orderReturnRepository;
        }

        /// <summary>
        /// 根据订单ID获得订单信息
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public async Task<OrderDetailOutDto> GetByIdAsync(int orderId) {
            var order = await Task.Run(() => _orderRepository.GetAllIncluding(c => c.OrderItems)
                .FirstOrDefault(c => c.Id == orderId));
            if(order == null) {
                throw new UserFriendlyException("请求失败!", "订单信息不存在!");
            }
            await CheckIsBlankOut(order);
            var dto = new OrderDetailOutDto();
            dto.PaymentMethod = (PaymentMethod)Enum.Parse(typeof(PaymentMethod), order.PaymentMethod, true);
            dto.IsPayed = order.IsPayed;
            dto.OrderItem = order.OrderItems.First().MapTo<OrderItemOutDto>();
            dto.OrderTime = order.OrderTime;
            dto.Price = order.Price;
            dto.IsBlankOut = order.IsBlankOut;
            dto.IsDelivered = order.IsDelivered;
            var userAddressEntity = await _userAddressRepository.FirstOrDefaultAsync(order.UserAddressId);
            dto.UserAddress = userAddressEntity.MapTo<UserAddressOutDto>();
            if(userAddressEntity != null) {
                dto.UserAddress.AreaData = (await _cityDomainService.GetFullAddressByCityId(userAddressEntity.AreaCode))
                    .MapTo<FullCityOutDto>();
            }
            dto.Id = order.Id;
            return dto;
        }

        private async Task<OrderListOutDto> Entity2ListDtoAsync(Order entity) {
            var dto = new OrderListOutDto();
            dto.Id = entity.Id;
            var orderItem = entity.OrderItems.First();
            dto.HeaderImageId =
                await AttachmentManager.GetIdAsync(Product.ProductHeaderAttachmentType, orderItem.ProductId);
            dto.IsPayed = entity.IsPayed;
            dto.OrderTime = entity.OrderTime;
            dto.Price = entity.Price;
            dto.IsBlankOut = entity.IsBlankOut;
            dto.ProductName = orderItem.ProductName;
            dto.IsDelivered = entity.IsDelivered;
            return dto;
        }

        /// <summary>
        /// 检查订单时间并作废
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        private async Task CheckIsBlankOut(Order order) {
            if(order.OrderTime.AddHours(24) < DateTime.Now) {
                order.IsBlankOut = true;
                await _orderRepository.UpdateAsync(order);
            }
        }

        /// <summary>
        /// 提交订单
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<int> SaveOrderAsync(OrderRequestDto input) {
            List<OrderItem> orderItems = new List<OrderItem>();
            var userType = await _agentDomainService.GetUserTypeAsync(AbpSession.GetUserId());
            foreach(var item in input.OrderItems) {
                var product = await _productRepository.FirstOrDefaultAsync(item.ProductId);
                if(product == null) {
                    throw new UserFriendlyException("请求失败!", "订单中有商品已被删除,请重新选择!");
                }
                if(product.Status == ProductStatus.不可售) {
                    throw new UserFriendlyException("请求失败!", $"订单中的商品{product.Name}已下架,请重新选择!");
                }

                var entity = new OrderItem();
                entity.Count = item.Count;
                entity.ProductId = item.ProductId;
                entity.UserType = userType;
                entity.AgentPriceOfTheTime = product.AgentPrice;
                entity.CostPriceOfTheTime = product.CostPrice;
                entity.PriceOfTheTime = product.ExFactoryPrice;
                entity.ProductName = product.Name;
                orderItems.Add(entity);

            }

            var order = new Order();
            order.AgentId = await _agentDomainService.GetUserAgentIdByUserIdAsync(AbpSession.GetUserId());
            order.OrderItems = orderItems;
            order.IsPayed = false;
            order.OrderTime = DateTime.Now;
            order.Price = orderItems.Sum(c => c.PriceOfTheTime);
            order.Remark = input.Remark;
            order.UserId = AbpSession.GetUserId();
            order.PaymentMethod = input.PaymentMethod.ToString();
            order.UserAddressId = input.UserAddressId;
            var orderId = await _orderRepository.InsertOrUpdateAndGetIdAsync(order);
            EventBus.Trigger(new OrderSavedEventData()
                {OrderId = orderId, ProductId = input.OrderItems.First().ProductId});

            return orderId;

        }

        /// <summary>
        /// 获取当前用户的所有订单
        /// </summary>
        /// <returns></returns>
        public async Task<PagedResultDto<OrderListOutDto>> GetOrdersAsync(GetOrdersRequestDto input) {
            var query = _orderRepository.GetAllIncluding(c => c.OrderItems)
                .WhereIf(!input.Keyword.IsNullOrWhiteSpace(),
                    c => c.OrderItems.Any(item => item.ProductName.Contains(input.Keyword)))
                .WhereIf(input.StartTime.HasValue, c => c.OrderTime > input.StartTime)
                .WhereIf(input.EndTime.HasValue, c => c.OrderTime < input.EndTime)
                .Where(c => c.UserId == AbpSession.UserId);
            var total = await Task.Run(() => query.Count());
            var paged = await Task.Run(() => query.OrderBy(input.Sorting).PageBy(input).ToList());
            var list = new List<OrderListOutDto>();
            foreach(var order in paged) {
                await CheckIsBlankOut(order);
                list.Add(await Entity2ListDtoAsync(order));
            }
            return new PagedResultDto<OrderListOutDto>(total, list);
        }

        /// <summary>
        /// 取消订单
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public async Task BlankOutOrderAsync(int orderId) {
            var order = await _orderRepository.FirstOrDefaultAsync(orderId);
            if(order.IsPayed) {
                throw new UserFriendlyException("取消订单失败!", "订单已支付,请申请退货!");
            }
            order.IsBlankOut = true;
            await _orderRepository.UpdateAsync(order);
        }

        /// <summary>
        /// 获取当前店长卖出的订单记录
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<OrderForAgentOutDto>> GetAgentOutOrdersAsync(GetOrdersRequestDto input) {
            var agent = await _agentDomainService.GetByUserIdAsync(AbpSession.GetUserId());
            if(agent == null) {
                throw new UserFriendlyException("请求失败!", "您还不是店长!");
            }
            var query = _orderRepository.GetAllIncluding(c => c.OrderItems, c => c.User)
                .WhereIf(!input.Keyword.IsNullOrWhiteSpace(),
                    c => c.OrderItems.Any(item => item.ProductName.Contains(input.Keyword)))
                .WhereIf(input.StartTime.HasValue, c => c.OrderTime > input.StartTime)
                .WhereIf(input.EndTime.HasValue, c => c.OrderTime < input.EndTime)
                .Where(c => c.AgentId == agent.Id);
            int total = await Task.Run(() => query.Count());
            var paged = await Task.Run(() => query.OrderBy(input.Sorting).PageBy(input).ToList());
            var dtos = new List<OrderForAgentOutDto>();
            foreach(var entity in paged) {
                await CheckIsBlankOut(entity);
                var dto = await Entity2ListDtoAsync(entity);
                var agentDto = dto.MapTo<OrderForAgentOutDto>();
                var orderItem = entity.OrderItems.First();
                agentDto.AgentProfit = (orderItem.PriceOfTheTime - orderItem.AgentPriceOfTheTime) * orderItem.Count;
                agentDto.Name = entity.User.Name;
                agentDto.PhoneNumber = entity.User.PhoneNumber;
                agentDto.WeChat = entity.User.WeChat;
                dtos.Add(agentDto);
            }
            return new PagedResultDto<OrderForAgentOutDto>(total, dtos);
        }

        /// <summary>
        /// 支付
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<Charge> PayAsync(PayRequestDto input) {
            var order = _orderRepository.GetAllIncluding(c => c.OrderItems).FirstOrDefault(c => c.Id == input.OrderId);
            if(order == null) {
                throw new UserFriendlyException("请求失败!", "订单不存在!");
            }
            if(order.OrderTime.AddHours(24) < DateTime.Now || order.IsBlankOut) {
                throw new UserFriendlyException("请求失败!", "订单已作废!");
            }
            if(order.IsPayed) {
                throw new UserFriendlyException("请求失败!", "此订单已经支付过了!");
            }
            var charge = await _pingPlusPlusDomainService.Pay(order, input.Channel,
                RequestInfo.RemoteAddress.Split(',').LastOrDefault());
            return charge;
        }

        /// <summary>
        /// 修改订单价格
        /// </summary>
        /// <returns></returns>
        public async Task ChangeOrderPriceAsync(ChangeOrderPriceInputDto input) {
            var order = await Task.Run(() => _orderRepository.GetAllIncluding(c => c.OrderItems)
                .FirstOrDefault(c => c.Id == input.Id));
            if(order == null) {
                throw new UserFriendlyException("修改价格失败!", "订单已不存在!");
            }
            await CheckIsBlankOut(order);
            if(order.IsBlankOut) {
                throw new UserFriendlyException("修改价格失败!", "下单时间超时,订单已作废!");
            }

            var isHasBeenInitiated = await _pingPlusPlusDomainService.IsHasBeenInitiatedAsync(input.Id);
            if(isHasBeenInitiated) {
                throw new UserFriendlyException("修改价格失败!", "订单已经发起支付请求!");
            }
            if(order.IsPayed) {
                throw new UserFriendlyException("修改价格失败!", "订单已支付!");
            }

            var user = await UserManager.GetUserByIdAsync(AbpSession.GetUserId());
            var currentAgent = await _agentDomainService.GetByUserIdAsync(AbpSession.GetUserId());
            if(currentAgent == null) {
                throw new UserFriendlyException("修改价格失败!", "您不是店长,无权修改!");
            }
            if(order.AgentId != currentAgent.Id || !user.IsInner) {
                throw new UserFriendlyException("修改价格失败!", "这个订单不是你的客户的,您无权修改!");
            }

            OrderPriceChange priceChange = new OrderPriceChange();
            priceChange.AfterPrice = input.Price;
            priceChange.BeforePrice = order.Price;
            priceChange.OrderId = order.Id;
            priceChange.Remark = input.Remark;
            await _priceChangeRepository.InsertOrUpdateAndGetIdAsync(priceChange);

            order.Price = input.Price;
            await _orderRepository.UpdateAsync(order);


        }

        /// <summary>
        /// 后台获取订单列表
        /// </summary>
        /// <returns></returns>
        public async Task<PagedResultDto<OrderForAdminOutDto>> GetOrdersForAdminAsync(GetOrdersForAdminInputDto input) {
            var query = _orderRepository.GetAllIncluding(c => c.OrderItems, c => c.User)
                .WhereIf(!input.UserKeyword.IsNullOrWhiteSpace(),
                    c => c.User.Name.Contains(input.UserKeyword) || c.User.WeChat.Contains(input.UserKeyword)
                         || c.User.PhoneNumber.Contains(input.UserKeyword))
                .WhereIf(!input.Keyword.IsNullOrWhiteSpace(),
                    c => c.OrderItems.Any(item => item.ProductName.Contains(input.Keyword)))
                .WhereIf(input.StartTime.HasValue, c => c.OrderTime > input.StartTime)
                .WhereIf(input.EndTime.HasValue, c => c.OrderTime < input.EndTime)
                .WhereIf(input.IsPayed.HasValue, c => c.IsPayed == input.IsPayed.Value);

            var total = await Task.Run(() => query.Count());
            var paged = await Task.Run(() => query.OrderBy(input.Sorting).PageBy(input).ToList());
            var list = new List<OrderForAdminOutDto>();
            foreach(var order in paged) {
                await CheckIsBlankOut(order);
                var dto = await Entity2ListDtoAsync(order);
                var adminDto = dto.MapTo<OrderForAdminOutDto>();
                var orderItem = order.OrderItems.First();
                adminDto.AgentProfit = order.Price - orderItem.AgentPriceOfTheTime * orderItem.Count;
                adminDto.SystemProfit = order.Price - (orderItem.AgentPriceOfTheTime - orderItem.CostPriceOfTheTime)
                                        * orderItem.Count;
                adminDto.Name = order.User.Name;
                adminDto.PhoneNumber = order.User.PhoneNumber;
                adminDto.WeChat = order.User.WeChat;
                adminDto.UserId = order.UserId;
                var agent = await Task.Run(() => _agentRepository.GetAllIncluding(c => c.User)
                    .FirstOrDefault(c => c.Id == order.AgentId));
                adminDto.AgentUserName = agent?.User.Name;
                list.Add(adminDto);
            }
            return new PagedResultDto<OrderForAdminOutDto>(total, list);
        }

        /// <summary>
        /// 订单发货
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task DeliverOrderAsync(OrderDeliverInputDto input) {
            var order = await _orderRepository.FirstOrDefaultAsync(input.OrderId);
            if(order == null) {
                throw new UserFriendlyException("发货操作失败!", "订单信息已不存在!");
            }

            await _orderDeliveryInfoRepository.InsertOrUpdateAndGetIdAsync(input.MapTo<OrderDeliveryInfo>());

            order.IsDelivered = true;
            await _orderRepository.UpdateAsync(order);
        }

        /// <summary>
        /// 后台获得订单详情
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public async Task<OrderDetailForAdminOutDto> GetOrderForAdminAsync(int orderId) {
            var order = await Task.Run(() => _orderRepository
                .GetAllIncluding(c => c.OrderItems, c => c.Agent, c => c.Agent.User, c => c.User)
                .FirstOrDefault(c => c.Id == orderId));
            if(order == null) {
                throw new UserFriendlyException("请求失败!", "订单信息不存在!");
            }
            await CheckIsBlankOut(order);
            var dto = new OrderDetailForAdminOutDto();
            var orderItem = order.OrderItems.First();
            dto.PaymentMethod = (PaymentMethod)Enum.Parse(typeof(PaymentMethod), order.PaymentMethod, true);
            dto.IsPayed = order.IsPayed;
            dto.OrderItem = await ItemMapedForAdmin(orderItem, order);
            dto.OrderTime = order.OrderTime;
            dto.Price = order.Price;
            dto.IsBlankOut = order.IsBlankOut;
            dto.IsDelivered = order.IsDelivered;
            var agentLeader = await _agentDomainService.GetAgentLeaderAsync(order.AgentId);
            dto.AgentLeader = agentLeader?.User.Name;
            dto.AgentName = order.Agent?.User.Name;
            dto.AgentUserId = order.Agent?.UserId;
            dto.UserName = order.User.Name;
            var userAddressEntity = await _userAddressRepository.FirstOrDefaultAsync(order.UserAddressId);
            dto.UserAddress = userAddressEntity.MapTo<UserAddressOutDto>();
            if(userAddressEntity != null) {
                dto.UserAddress.AreaData = (await _cityDomainService.GetFullAddressByCityId(userAddressEntity.AreaCode))
                    .MapTo<FullCityOutDto>();
            }
            dto.Id = order.Id;
            return dto;
        }

        /// <summary>
        /// 申请退货
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task ApplyReturnOrderAsync(OrderReturnInputDto input) {
            var order = await _orderRepository.FirstOrDefaultAsync(input.OrderId);
            if(order.UserId != AbpSession.UserId) {
                throw new UserFriendlyException("申请退货出错!", "不是您的订单,你不能操作!");
            }
            if(order.IsBlankOut) {
                throw new UserFriendlyException("申请退货出错!", "订单已作废!");
            }

            await _orderReturnRepository.InsertOrUpdateAndGetIdAsync(input.MapTo<OrderReturn>());
        }

        /// <summary>
        /// 取消退货申请
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public async Task CancelOrderReturnApplyAsycn(int orderId) {
            var entity = await _orderReturnRepository.FirstOrDefaultAsync(c => c.OrderId == orderId);
            if(entity == null) {
                throw new UserFriendlyException("退货申请取消操作失败!", "退货申请不存在!");
            }

            await _orderReturnRepository.DeleteAsync(entity);
        }

        /// <summary>
        /// 获得订单退货信息
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public async Task<OrderReturnOutputDto> GetOrderReturnInfoAysnc(int orderId) {
            var entity = await _orderReturnRepository.FirstOrDefaultAsync(c => c.OrderId == orderId);
            return entity.MapTo<OrderReturnOutputDto>();
        }

        /// <summary>
        /// 店长退货信息确认
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task ConfirmOrderReturnAsync(OrderReturnConfirmInputDto input) {
            var entity = await _orderReturnRepository.FirstOrDefaultAsync(c => c.OrderId == input.OrderId);
            if (entity == null)
            {
                throw new UserFriendlyException("操作失败!", "退货信息不存在!");
            }
            entity.IsAgreed = input.IsAgreed;
            entity.IsConfirmed = true;
            entity.UserAddressId = input.UserAddressId;
            await _orderReturnRepository.UpdateAsync(entity);
        }

        /// <summary>
        /// 客户填写退货快递信息
        /// </summary>
        public async Task OrderReturnExpressInputAysnc(OrderReturnExpressInputDto input) {
            var entity = await _orderReturnRepository.FirstOrDefaultAsync(c => c.OrderId == input.OrderId);
            if (entity == null)
            {
                throw new UserFriendlyException("操作失败!", "退货信息不存在!");
            }
            entity.ExpressCode = input.ExpressCode;
            entity.ExpressCompany = input.ExpressCompany;
            await _orderReturnRepository.UpdateAsync(entity);
        }

        /// <summary>
        /// 店长退货收货确认
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task OrderReturnReceivedAsync(OrderReturnReceivedInputDto input) {
            var entity = await _orderReturnRepository.FirstOrDefaultAsync(c => c.OrderId == input.OrderId);
            if(entity == null) {
                throw new UserFriendlyException("操作失败!", "退货信息不存在!");
            }

            entity.IsReceived = input.IsReceived;
            entity.RealReturnMoney = input.RealReturnMoney;
            await _orderReturnRepository.UpdateAsync(entity);

            await _pingPlusPlusDomainService.Return(entity);
        }

        private async Task<OrderItemForAdminOutDto> ItemMapedForAdmin(OrderItem orderItem, Order order) {
            var itemForAdmin = orderItem.MapTo<OrderItemForAdminOutDto>();
            itemForAdmin.AgentProfit = order.Price - orderItem.AgentPriceOfTheTime * orderItem.Count;
            itemForAdmin.SystemProfit = order.Price - orderItem.AgentPriceOfTheTime * orderItem.Count
                                        - orderItem.CostPriceOfTheTime * orderItem.Count;
            var product = await Task.Run(() => _productRepository.GetAllIncluding(c => c.Category)
                .FirstOrDefault(c => c.Id == orderItem.ProductId));
            itemForAdmin.ProductName = product?.Name;
            itemForAdmin.CategoryName = product?.Category?.Name;
            itemForAdmin.ProductType = product?.ProductType;
            return itemForAdmin;
        }
    }
}